/*  -*- mode: c++; coding: utf-8; c-file-style: "stroustrup"; -*-

    Copyright 2008 Asier Aguirre <asier.aguirre@gmail.com>
    This file is part of Intromit.

    Intromit is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Intromit is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Intromit.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "ogl.hh"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>


#define OGL_WINDOW_MARK      -10001
#define OGL_MAX_EXT             128
#define OGL_MAX_WINPOS_CORRECT   64


CLASS_INIT(ogl_window); // OGL_WINDOW CLASS

ogl_window::ogl_window()
{
    CLASS_CTOR(ogl_window);
    ctor();
}

ogl_window::~ogl_window()
{
    CLASS_DTOR(ogl_window);
    dtor();
}

void ogl_window::ctor()
{
    o=NULL;
    w=0;

    // default cursor
    default_cursor=OGL_CURSOR_SELECTION;

    // init variables
    name=NULL;
    managed=true;
    x=y=OGL_WINDOW_MARK;
    width=640;
    height=480;
}

void ogl_window::dtor()
{
    if(w) destroy_window();
    if(name) name=(char*)realloc(name, 0);
    if(o) o=(ogl*)o->decref();
}

bool ogl_window::init()
{
    return init(managed, name);
}

bool ogl_window::init(bool managed, char *name)
{
    if(!o)
    {
	o=new ogl;
	if(!o->init()) return false;
    }
    ogl *o_=o;
    o=NULL;
    bool dev=init(o_, managed, name);
    o_->decref();
    return dev;
}

bool ogl_window::init(ogl *o)
{
    return init(o, managed, name);
}

bool ogl_window::init(ogl *o_, bool managed_, char *name_)
{
    // ogl: protect against ogl deletion
    o_->incref();
    if(o) o=(ogl*)o->decref();
    o=o_;
    // try init ogl if necesary
    if(!o->get_dpy() && !o->init()) ERROR_RX(false, "ogl init");

    // X window
    managed=managed_;
    if(w) destroy_window();
    if(!(w=create_X_window(managed))) ERROR_RX(false, "create_X_window");

    // name, size
    if(!set_name(name_)) ERROR_RX(false, "set_name");
    if(!set_size(width, height)) ERROR_RX(false, "set_size");

    // map and position window
    map();
    sync();
    if(!set_position(x, y)) ERROR_R0("set_position");
    return true;
}

ogl *ogl_window::check_ogl()
{
    if(!o) ERROR_R0("ogl: suggest, not initialized");
    return o;
}

Window ogl_window::create_X_window(bool managed)
{
    XSetWindowAttributes attr;
    unsigned long mask;

    Display *dpy=o->get_dpy();
    XVisualInfo *visinfo=o->get_visinfo();
    if(!dpy || !visinfo) return 0;

    attr.background_pixel = 0;
    attr.border_pixel = 0;
    attr.colormap = XCreateColormap(dpy, RootWindow(dpy, DefaultScreen(dpy)), visinfo->visual, AllocNone);
    attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask;
    attr.backing_store = Always;
    attr.backing_planes = attr.backing_pixel = 0xffffffff;
    attr.save_under=True;
    attr.override_redirect = (managed==false);
    mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask | CWOverrideRedirect
#if 1
        | CWBackingStore | CWBackingPlanes | CWBackingPixel | CWSaveUnder
#endif
        ;

    return XCreateWindow(dpy, RootWindow(dpy, DefaultScreen(dpy)),
			 0, 0, 16, 16,
			 0, visinfo->depth, InputOutput,
			 visinfo->visual, mask, &attr);
}

void ogl_window::destroy_window()
{
    Display *dpy;
    if(w && check_ogl() && (dpy=o->get_dpy()) && !XDestroyWindow(dpy, w)) ERROR("XDestroyWindow");
    w=0;
}

bool ogl_window::set_size(int width_, int height_)
{
    if(width_<1 || height<1) ERROR_RX(false, "size out of bounds");

    Display *dpy;
    width=width_;
    height=height_;
    if(width!=OGL_WINDOW_MARK && height!=OGL_WINDOW_MARK &&
       check_ogl() && (dpy=o->get_dpy()) && !XResizeWindow(dpy, w, width, height))
	ERROR_RX(false, "XResizeWindow");
    return true;
}

bool ogl_window::set_position_no_check(int x_, int y_)
{
    Display *dpy;
    if(check_ogl() && (dpy=o->get_dpy()))
    {
	if(x_==OGL_WINDOW_MARK && y_==OGL_WINDOW_MARK) return true;

	if(!XMoveWindow(dpy, w, x_, y_)) ERROR_RX(false, "XMoveWindow");
	sync();
    }
    return true;
}

bool ogl_window::set_position(int x_, int y_)
{
    Display *dpy;
    if(check_ogl() && (dpy=o->get_dpy()))
    {
	if(x_==OGL_WINDOW_MARK && y_==OGL_WINDOW_MARK) return true;

	int xtmp=0, ytmp=0;
	if(!set_position_no_check(x_, y_)) ERROR_RX(false, "set_position_no_check");

	XEvent event;
	while(XCheckWindowEvent(o->get_dpy(), w, OGL_CONFIGURE_EVENT, &event) && event.type==ConfigureNotify) usleep(500);
	if(managed)
	{
	    if(!get_position(&xtmp, &ytmp)) ERROR_RX(false, "get position");
	    if(xtmp!=x_ || ytmp!=y_)
	    {
		// correction
		int diffx=xtmp-x_;
		int diffy=ytmp-y_;
		if(diffx>-OGL_MAX_WINPOS_CORRECT && diffx<OGL_MAX_WINPOS_CORRECT &&
		   diffy>-OGL_MAX_WINPOS_CORRECT && diffy<OGL_MAX_WINPOS_CORRECT)
		    if(!set_position_no_check(x_-diffx, y_-diffy)) ERROR_RX(false, "set_position_no_check");
	    }
	}
	x=x_;
	y=y_;
	while(XCheckWindowEvent(o->get_dpy(), w, OGL_CONFIGURE_EVENT, &event) && event.type==ConfigureNotify);
	return true;
    }
    return false;
}

bool ogl_window::get_position(int *x_, int *y_)
{
    Display *dpy;
    if(check_ogl() && (dpy=o->get_dpy()))
    {
	int x_tmp, y_tmp, tmp1, tmp2;
	Window root;
	if(!get_geometry(w, &x_tmp, &y_tmp, &tmp1, &tmp2, &root)) return false;

	Window child;
	if(!XTranslateCoordinates(dpy, w, root, -x_tmp, -y_tmp, x_, y_, &child))
	    ERROR_RX(false, "XTranslateCoordinates");
	x=*x_;
	y=*y_;
	return true;
    }
    return false;
}

bool ogl_window::set_name(char *name_)
{
    if(name_!=name) // protect against own name deletion
    {
	if(name) name=(char*)realloc(name, 0);
	if(name_) name=strdup(name_);
    }

    Display *dpy;
    if(check_ogl() && (dpy=o->get_dpy()))
    {
	char *n=(name)?name:(char*)"";
	XClassHint class_hints={n, n};
	if(!XSetClassHint(dpy, w, &class_hints)) ERROR_RX(false, "XSetClassHint");
	if(!XStoreName(dpy, w, n)) ERROR_RX(false, "XStoreName");
	return true;
    }
    return false;
}


void ogl_window::map()
{
    Display *dpy;
    if(check_ogl() && (dpy=o->get_dpy())) XMapWindow(dpy, w);
}

void ogl_window::sync()
{
    Display *dpy;
    if(check_ogl() && (dpy=o->get_dpy())) XSync(dpy, False);
}

void ogl_window::update_state_from_window()
{
    if(o)
    {
	int dx, dy;
	get_size(&dx, &dy);
	get_position(&dx, &dy);
    }
}

bool ogl_window::get_size(int *width_, int *height_)
{
    int xtmp, ytmp;
    if(!get_geometry(w, &xtmp, &ytmp, width_, height_)) return false;
    width=*width_; height=*height_;
    return true;
}

bool ogl_window::get_root_size(int *x, int *y)
{
    Display *dpy;
    int tmp1, tmp2;
    if(check_ogl() && (dpy=o->get_dpy()))
	return get_geometry(RootWindow(dpy, DefaultScreen(dpy)), &tmp1, &tmp2, x, y);
    return false;
}

bool ogl_window::get_geometry(Window w, int *x, int *y, int *width, int *height, Window *root)
{
    Display *dpy;
    if(check_ogl() && (dpy=o->get_dpy()))
    {
	unsigned int border, depth;
	Window root_win;
	if(!XGetGeometry(dpy, w, &root_win, x, y, (unsigned int*)width, (unsigned int*)height, &border, &depth))
	    ERROR_RX(false, "XGetGeometry");
	if(root) *root=root_win;
	return true;
    }
    return false;
}

bool ogl_window::reparent(Window parent, int x, int y)
{
    Display *dpy;
    if(check_ogl() && (dpy=o->get_dpy()))
    {
	if(!XReparentWindow(dpy, w, parent, x, y)) ERROR_RX(false, "XReparentWindow");
	return true;
    }
    return false;
}

bool ogl_window::set_gl_ctx()
{
    Display *dpy;
    GLXContext glx_ctx;
    if(o && (dpy=o->get_dpy_no_check()) && (glx_ctx=o->get_glx_ctx()))
	return put_glx_ctx(dpy, glx_ctx, w);
    return false;
}

bool ogl_window::unset_gl_ctx()
{
    Display *dpy;
    if(o && (dpy=o->get_dpy_no_check())) put_glx_ctx(dpy, NULL, w);
    return false;
}

bool ogl_window::put_glx_ctx(Display *dpy, GLXContext glx_ctx, Window w)
{
    if(!glXMakeCurrent(dpy, glx_ctx?w:0, glx_ctx))
	ERROR_RX(false, "glXMakeCurrent(ctx=%p)\n", glx_ctx);
    return true;
}

void ogl_window::gl_test()
{
    glClearColor(0.2, 0.0, 0.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);

    int viewport[4];
    glGetIntegerv(GL_VIEWPORT, viewport);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    glOrtho(viewport[0], viewport[2], viewport[1], viewport[3], -400, 400);

    double mc[16];
    glGetDoublev(GL_PROJECTION_MATRIX, mc);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    glTranslatef(20, 20, -20);
    //glScalef(0.7, 0.7, 0.7);
    //glScalef(2, 4, 2);
    //glRotatef(60, 0, 1, 0);
    //glRotatef(30, 0, 0, 1);

    glColor3f(0.8, 0.3, 0.1);
}

void ogl_window::gl_swap()
{
    Display *dpy;
    if(o && (dpy=o->get_dpy_no_check())) glXSwapBuffers(dpy, w);
}

bool ogl_window::set_events(int mask)
{
    Display *dpy;
    if(check_ogl() && (dpy=o->get_dpy()))
    {
	XSetWindowAttributes attr;
	unsigned long masc;

	masc = CWEventMask;
	attr.event_mask=mask;
	if(!XChangeWindowAttributes(dpy, w, masc, &attr))
	    ERROR_RX(false, "base_glx_events: error: XChangeWindowAttributes");
	return true;
    }
    return false;
}

bool ogl_window::set_cursor(int c)
{
    Display *dpy;
    if(check_ogl() && (dpy=o->get_dpy()))
	if(XDefineCursor(dpy, w, (c==OGL_CURSOR_IMPOSSIBLE)?None:o->get_cursor(c))<=0)
	    ERROR_RX(false, "XDefineCursor");
    default_cursor=c;
    return true;
}

char *ogl_window::extensions_next(char *ext)
{
    if(!ext) return (char*)glGetString(GL_EXTENSIONS);
    if((ext=strchr(ext, ' ')) && ext[1]) return ext+1;
    return NULL;
}

char *ogl_window::extensions_only_one(char *ext)
{
    static char buffer[OGL_MAX_EXT];

    char *spc;
    int size=OGL_MAX_EXT-1;
    if((spc=strchr(ext, ' ')) && (int)spc-(int)ext<size) size=(int)spc-(int)ext;

    strncpy(buffer, ext, size);
    buffer[size]=0;
    return buffer;
}

void ogl_window::extensions_print_common(char *needle)
{
    set_gl_ctx();
    int n_ext=0;
    char *ext=NULL;
    printf("Extensions:\n");
    while((ext=extensions_next(ext)))
    {
	char *buffer=extensions_only_one(ext);
	if(!needle || strstr(buffer, needle))
	{
	    printf("\t%s\n", buffer);
	    n_ext++;
	}
    }
    printf("Total: %d\n", n_ext);
    unset_gl_ctx();
}

void ogl_window::extensions_print()
{
    extensions_print_common();
}

void ogl_window::extensions_print_search(char *needle)
{
    extensions_print_common(needle);
}

char *ogl_window::extensions_search(char *needle)
{
    set_gl_ctx();
    char *ext=extensions_next(NULL);
    unset_gl_ctx();
    while((ext=extensions_next(ext)))
    {
	char *buffer=extensions_only_one(ext);
	if(strstr(buffer, needle)) return buffer;
    }
    return NULL;
}

XEvent *ogl_window::get_event(XEvent *event, int mask)
{
    while(XCheckMaskEvent(o->get_dpy(), mask, event)) // XCheckWindowEvent(o->get_dpy(), w, mask, event)
    {
	if(event->type==ConfigureNotify)
	{
	    if(!event->xconfigure.send_event) continue;

	    // mark resize to user apps in send_event flag
	    bool resize;
	    if(width!=event->xconfigure.width || height!=event->xconfigure.height)
		resize=true; else resize=false;
	    event->xconfigure.send_event=resize;

	    x=event->xconfigure.x;
	    y=event->xconfigure.y;
	    width=event->xconfigure.width;
	    height=event->xconfigure.height;
	}
	return event;
    }
    return NULL;
}

Display *ogl_window::get_dpy()
{
    return o->get_dpy();
}


CLASS_INIT(ogl); // OGL CLASS

ogl::ogl()
{
    CLASS_CTOR(ogl);
    ctor();
}

ogl::~ogl()
{
    CLASS_DTOR(ogl);
    dtor();
}

void ogl::ctor()
{
    // init variables
    dpy=NULL;
    glx_ctx=0;
    visinfo=NULL;
    cursor=NULL;
    n_cursor=0;
}

void ogl::dtor()
{
    // free cursors
    if(dpy) for(int i=0; i<n_cursor; i++) XFreeCursor(dpy, cursor[i]);
    if(cursor) cursor=(Cursor*)realloc(cursor, 0);
    n_cursor=0;

    if(dpy)
    {
	// set keyboard autorepetition on at exit
	XAutoRepeatOn(dpy);

	XCloseDisplay(dpy);
	dpy=NULL;
    }
}

bool ogl::init()
{
    // X connection
    if(!open_X_connection()) ERROR_RX(false, "open_X_connection");
    Display *dpy_=dpy;
    dpy=NULL;
    return init_dpy(dpy_);
}

bool ogl::init_dpy(Display *dpy_)
{
    if(dpy) XCloseDisplay(dpy);
    dpy=dpy_;
    if(!create_glx_context(1, 1)) ERROR_RX(false, "create_glx_context");

    // create default cursors
    if(!init_cursors()) ERROR_RX(false, "creating cursors");

    return true;
}

bool ogl::init_cursors()
{
    static char lupa_bits[] = {
	0x00, 0x00, 0xc0, 0x03, 0x30, 0x0c, 0x08, 0x10, 0x84, 0x21, 0x84, 0x21,
	0x82, 0x41, 0xf2, 0x4f, 0xf2, 0x4f, 0x82, 0x41, 0x84, 0x21, 0x84, 0x21,
	0x08, 0x10, 0x30, 0x0c, 0xc0, 0x03, 0x00, 0x00};

    static char lupamask_bits[] = {
	0xc0, 0x03, 0xf0, 0x0f, 0xf8, 0x1f, 0xbc, 0x3d, 0xce, 0x73, 0xce, 0x73,
	0xf7, 0xef, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xef, 0xce, 0x73, 0xce, 0x73,
	0xbc, 0x3d, 0xf8, 0x1f, 0xf0, 0x0f, 0xc0, 0x03};

    static char cruz_bits[] = {
	0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00,
	0x80, 0x00, 0xff, 0xff, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00,
	0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00};

    static char cruzmask_bits[] = {
	0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01,
	0x80, 0x01, 0xff, 0xff, 0xff, 0xff, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01,
	0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01};

    static char aspa_bits[] = {
	0x00, 0x00, 0x1e, 0x78, 0x06, 0x60, 0x0a, 0x50, 0x12, 0x48, 0x20, 0x04,
	0x40, 0x02, 0x80, 0x00, 0x00, 0x01, 0x40, 0x02, 0x20, 0x04, 0x12, 0x48,
	0x0a, 0x50, 0x06, 0x60, 0x1e, 0x78, 0x00, 0x00};

    static char aspamask_bits[] = {
	0x1e, 0x78, 0x3f, 0xfc, 0x1f, 0xf8, 0x1f, 0xf8, 0x3f, 0xfc, 0x72, 0x4e,
	0xe0, 0x07, 0xc0, 0x03, 0xc0, 0x03, 0xe0, 0x07, 0x72, 0x4e, 0x3f, 0xfc,
	0x1f, 0xf8, 0x1f, 0xf8, 0x3f, 0xfc, 0x1e, 0x78};

    static char reloj_bits[] = {
	0x00, 0x00, 0xfe, 0x3f, 0x02, 0x20, 0x04, 0x10, 0x18, 0x0c, 0x70, 0x07,
	0xe0, 0x03, 0xc0, 0x01, 0xc0, 0x01, 0xa0, 0x02, 0x90, 0x04, 0x88, 0x08,
	0xc4, 0x11, 0xe2, 0x23, 0xfe, 0x3f, 0x00, 0x00};

    static char relojmask_bits[] = {
	0xfe, 0x3f, 0xff, 0x7f, 0xff, 0x7f, 0x0e, 0x38, 0x3c, 0x1e, 0xf8, 0x0f,
	0xf0, 0x07, 0xe0, 0x03, 0xe0, 0x03, 0xf0, 0x07, 0xb8, 0x0e, 0xdc, 0x1d,
	0xee, 0x3b, 0xff, 0x7f, 0xff, 0x7f, 0xfe, 0x3f};

    static char selector_bits[] = {
	0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0xe0, 0x0f,
	0x20, 0x08, 0x20, 0x08, 0x3e, 0xf8, 0x20, 0x08, 0x20, 0x08, 0xe0, 0x0f,
	0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01};

    static char selectormask_bits[] = {
	0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0xf0, 0x0f, 0xf0, 0x0f,
	0x30, 0x0c, 0x3f, 0xfc, 0x3f, 0xfc, 0x30, 0x0c, 0xf0, 0x0f, 0xf0, 0x0f,
	0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01};

    static char none_bits[] = {
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

    struct
    {
	char *bits, *maskbits;
    } cursores[OGL_CURSOR_LAST+1]=
      {{lupa_bits, lupamask_bits},
       {cruz_bits, cruzmask_bits},
       {aspa_bits, aspamask_bits},
       {reloj_bits, relojmask_bits},
       {selector_bits, selectormask_bits},
       {none_bits, none_bits},
       {NULL, NULL}
      };

    if(n_cursor) return true; // cursors already initialized
    if(!dpy) ERROR_R0("no cursors w/o dpy");

    n_cursor=OGL_CURSOR_LAST;
    cursor=(Cursor*)realloc(cursor, n_cursor*sizeof(Cursor));

    XColor fore, back;
    memset(&fore, 0, sizeof(XColor));
    memset(&back, 0, sizeof(XColor));
    fore.red=fore.green=fore.blue=65535;
    fore.flags=DoRed | DoGreen | DoBlue;

    for(int i=0; cursores[i].bits; i++)
    {
	Pixmap source, mask;
	source=XCreateBitmapFromData(dpy, RootWindow(dpy, DefaultScreen(dpy)), cursores[i].bits, 16, 16);
	mask=XCreateBitmapFromData(dpy, RootWindow(dpy, DefaultScreen(dpy)), cursores[i].maskbits, 16, 16);
	if(source==None || mask==None) ERROR_R0("no storage for bitmaps");

	cursor[i]=XCreatePixmapCursor(dpy, source, mask, &fore, &back, 8, 8);

	XFreePixmap(dpy, source);
	XFreePixmap(dpy, mask);
    }
    return true;
}


bool ogl::open_X_connection()
{
    if(!(dpy=XOpenDisplay(getenv("DISPLAY")))) ERROR_R0("XOpenDisplay");
    return true;
}

Display *ogl::check_dpy()
{
    if(!dpy) ERROR("no Display");
    return dpy;
}

GLXContext ogl::check_glx_ctx()
{
    if(!check_dpy()) return 0;
    if(!glx_ctx) ERROR("no glx context");
    return glx_ctx;
}

XVisualInfo *ogl::check_visinfo()
{
    if(!check_dpy()) return 0;
    if(!visinfo) ERROR("no visual");
    return visinfo;
}

bool ogl::create_glx_context(int z_buffer, int double_buffer)
{
    int attrib[]={GLX_RGBA, None};
    int attrib_zbuffer[]={GLX_RGBA, GLX_DEPTH_SIZE, 1, None};
    int attrib_dbuffer[]={GLX_RGBA, GLX_DOUBLEBUFFER, None};
    int attrib_zdbuffer[]={GLX_RGBA, GLX_DOUBLEBUFFER, GLX_DEPTH_SIZE, 1, None};
    int *p_attrib;

    if(z_buffer) {if(double_buffer) p_attrib=attrib_zdbuffer; else p_attrib=attrib_zbuffer;}
    else {if(double_buffer) p_attrib=attrib_dbuffer; else p_attrib=attrib;}

    if(!check_dpy()) return false;

    if(visinfo || glx_ctx) ERROR("warning: visinfo or glx_ctx lost");

    if(!(glXQueryExtension(dpy, NULL, NULL))) ERROR_R0("glXQueryExtension");
    if(!(visinfo=glXChooseVisual(dpy, DefaultScreen(dpy), p_attrib))) ERROR_R0("glXChooseVisual");
    if(!(glx_ctx=glXCreateContext(dpy, visinfo, NULL, True))) ERROR_R0("glXCreateContext");

    if(!glXIsDirect(dpy, glx_ctx)) ERROR("Warning: not a DirectRender openGL context");

    DEBUG("OGL context RGBA %s ZBuffer %s DoubleBuffer", z_buffer?"with":"w/o", double_buffer?"with":"w/o");
    return true;
}

ogl_window *ogl::new_window(bool managed, char *name)
{
    ogl_window *v=new ogl_window;
    v->init(this, managed, name);
    return v;
}

void ogl::sync()
{
    if(check_dpy()) XSync(dpy, False);
}

// @~select{
static XErrorHandler old_handler=NULL;
static XIOErrorHandler old_handler_io=NULL; // @}

void ogl::debug_mode()
{
    if(!check_dpy()) ERROR_R("debug_mode");

    XSynchronize(dpy, True);

    XErrorHandler handler;
    XIOErrorHandler handler_io;
    handler=XSetErrorHandler(X_handler_error);
    handler_io=XSetIOErrorHandler(X_handler_io_error);
    if(!old_handler) old_handler=handler;
    if(!old_handler_io) old_handler_io=handler_io;

    DEBUG("OGL X Debug mode enabled!");
}

int ogl::X_handler_error(Display *dpy, XErrorEvent *error)
{
    ERROR("XError: process stopped for debugging purposes");
    getchar();

    return (*old_handler)(dpy, error);
}

int ogl::X_handler_io_error(Display *dpy)
{
    ERROR("XError_io: process stopped for debugging purposes");
    getchar();

    return (*old_handler_io)(dpy);
}

void ogl::keyboard_repetition(bool yesno)
{
    if(!check_dpy()) ERROR_R("keyboard repetition");

    if(yesno) XAutoRepeatOn(dpy); else XAutoRepeatOff(dpy);
}
